﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;

using dotNails.Util;
using dotNails.CodeGen.Models;
using dotNails.CodeGen.Models.dotNails;
using dotNails.CodeGen.Util;
using System.Xml.Linq;
using System.Collections;
using EnvDTE;
using System.IO;
using System.Reflection;
using System.Data;
using System.Collections.ObjectModel;
using dotNails.CodeGen.T4;

namespace dotNails.UI {
	/// <summary>
	/// Interaction logic for DBManager.xaml
	/// </summary>
	public partial class DBManager : UserControl, IChildUserControl {
		private DbmlData _dbml;
		private IUserControlParent _parent;
		private bool _settingSelectAllTables = false;
		private bool _settingSelectAllViews = false;
		private bool _settingSelectAllProcs = false;
		// TableIDs are just the names of the table (i.e. Table.Type.Name == Table.Type.Id) - not sure why original team didn't just use the name.
		private Dictionary<string, string> MappedTableIDs = new Dictionary<string, string>();
				
		private List<Microsoft.SqlServer.Management.Smo.Table> _tables = new List<Microsoft.SqlServer.Management.Smo.Table>();

		public DBManager(IUserControlParent parent, DbmlData dbml) {
			InitializeComponent();
			_parent = parent;
			_dbml = dbml;
			TitleBar.Text = "Add to " + _dbml.Name;
		}

		//private void Window_Loaded(object sender, RoutedEventArgs e) {
		public void Display() {
			chkGenerateModel.IsChecked = true;
			chkGenerateController.IsChecked = false; 
			chkGenerateViews.IsChecked = false;

			_tables = SmoUtil.GetAllTablesNotInDbml(_dbml);
			Tables.ItemsSource = _tables;
			Tables.SelectedIndex = -1;
		}

		void btnBack_Click(object sender, RoutedEventArgs e) {
			// Find all dbmls in the solution
			List<DbmlData> dbmls = AddInUtil.GetAllDBMLsInSolution(_parent.DTE);

			UserControl previousUserControl = new DatabaseList(_parent, dbmls);
			_parent.LoadUserControl(previousUserControl);
		}

		private void tabTables_GotFocus(object sender, RoutedEventArgs e) {
			ToggleSelectAllVisibility(DbManagerTabs.Tables);
			if (Tables.Items.Count <= 0) {
				LoadTables();
			}
		}

		private void LoadTables() {
			_tables = SmoUtil.GetAllTablesNotInDbml(_dbml);
			ObservableCollection<Microsoft.SqlServer.Management.Smo.Table> tables = new ObservableCollection<Microsoft.SqlServer.Management.Smo.Table>(_tables);
			
			Tables.SelectedIndex = -1;
			Tables.ItemsSource = tables;
		}

		private void ClearTables() {
			Tables.SelectedIndex = -1;
			Tables.ItemsSource = null;
			Tables.Items.Clear();
		}

		private void tabViews_GotFocus(object sender, RoutedEventArgs e) {
			ToggleSelectAllVisibility(DbManagerTabs.Views);
			if (Views.Items.Count <= 0) {
				LoadViews();
			}
		}

		private void LoadViews() {
			// Whip up an anonymous typed List
			var view = new { Name = "" };
			var views = (new[] { view }).ToList();
			views.RemoveAt(0);

			views.Add(new { Name = "View" });
			views.Add(new { Name = "View2" });
			views.Add(new { Name = "View3" });

			Views.SelectedIndex = -1;
			Views.ItemsSource = views;
		}

		private void ClearViews() {
			Views.SelectedIndex = -1;
			Views.ItemsSource = null;
			Views.Items.Clear();
		}

		private void tabStoredProcs_GotFocus(object sender, RoutedEventArgs e) {
			ToggleSelectAllVisibility(DbManagerTabs.Procs);
			if (StoredProcs.Items.Count <= 0) {
				LoadStoredProcs();
			}
			//busyAnimation.Visibility = busyAnimation.Visibility == Visibility.Visible ? Visibility.Hidden : Visibility.Visible;
		}

		private void LoadStoredProcs() {
			ObservableCollection<Microsoft.SqlServer.Management.Smo.StoredProcedure> smoProcs = new ObservableCollection<Microsoft.SqlServer.Management.Smo.StoredProcedure>(SmoUtil.GetAllStoredProceduresNotInDbml(_dbml));
			
			StoredProcs.SelectedIndex = -1;
			StoredProcs.ItemsSource = smoProcs;
			busyAnimation.Visibility = Visibility.Hidden;
		}

		private void ClearStoredProcs() {
			StoredProcs.SelectedIndex = -1;
			StoredProcs.ItemsSource = null;
			StoredProcs.Items.Clear();
		}

		private void MapProcToClass(object sender, RoutedEventArgs e) {
			Button button = e.OriginalSource as Button;
			string currentResultClass = string.Empty;

			if (MappedTableIDs.ContainsKey(button.CommandParameter.ToString())) {
				currentResultClass = MappedTableIDs[button.CommandParameter.ToString()];
			}
			//Function selectedFunction = _dbml.DB.Functions.Find(f => f.Method == button.CommandParameter.ToString());
			//if (selectedFunction != null && selectedFunction.ElementType != null) {
			//    currentResultClass = selectedFunction.ElementType.IdRef;
			//}

			// Open up a dialog to select the Type
			ResultClassSelector.DbmlData = _dbml;
			ResultClassSelector.LoadData(button.CommandParameter.ToString(), currentResultClass);
			ResultClassSelector.ResultClassSaved += new ResultClassSelector.ResultClassSavedHandler(ResultClassSelector_ResultClassSaved);
			ResultClassSelector.Visibility = Visibility.Visible;
		}

		//private void StoredProcs_SelectionChanged(object sender, SelectionChangedEventArgs e) {
			//MessageBox.Show("Clicked something");
		//}

		protected void ResultClassSelector_ResultClassSaved(object sender, string resultClass, string procedureName) {
			// Add the selected class as a mapping for this procedure
			if (MappedTableIDs.ContainsKey(procedureName)) {
				MappedTableIDs.Remove(procedureName);
			}
			MappedTableIDs.Add(procedureName, resultClass);

			// Display the resultClass next to the proc in the StoredProc list
			//foreach (dotNails.CodeGen.Models.Table table in StoredProcs.Items) {
			//    if (MappedTableIDs.ContainsKey(table.Type.Name)) {
					
			//    }
			//}
		}

		void btnGenerate_Click(object sender, RoutedEventArgs e) {
			try {
				DbmlData newDbml = new DbmlData();
				newDbml.Name = _dbml.Name;
				newDbml.DB = new DataBase();
				newDbml.DB.Name = _dbml.DB.Name;
				newDbml.DB.Class = _dbml.DB.Class;
				newDbml.DB.XmlNamespace = _dbml.DB.XmlNamespace;
				newDbml.DB.Connection = _dbml.DB.Connection;
				newDbml.DB.Tables = new List<dotNails.CodeGen.Models.Table>();

				// ************** TABLES *********************
				foreach (Microsoft.SqlServer.Management.Smo.Table smoTable in Tables.SelectedItems) {
					dotNails.CodeGen.Models.Table dbmlTable = SmoUtil.GetDbmlTableFromSmoTable(smoTable);
					
					newDbml.DB.Tables.Add(dbmlTable);
				}

				// Pair table relationships by looking at foreign keys - see if both tables in a foreign key relationship are
				//  present in our dbml.
				DbmlUtil.PairTableRelationships(newDbml.DB.Tables);

				// Determine if any of the new tables have foreign key relationships with any existing tables in the dbml.
				//  If so, we need to re-sync those tables in order to properly handle their associations (like 
				//  Sql Enterprise Manager does when you save a table).
				List<dotNails.CodeGen.Models.Table> affectedDmblTables = GetRelatedDbmlTables(_dbml, newDbml);
				newDbml.DB.Tables.AddRange(affectedDmblTables);

				//  Pair relationships again, this time taking into account the tables already existing in the dbml
				DbmlUtil.PairTableRelationships(newDbml.DB.Tables);

				// Populate Associations from all paired table relationships
				DbmlUtil.PopulateAssociations(newDbml.DB.Tables);


				// ************** STORED PROCEDURES *********************
				// Get selected Stored procedures and their return types (create any classes we need to create for new result types)
				using (SqlConnection conn = new SqlConnection(_dbml.DB.Connection.ConnectionString)) {
					conn.Open();

					foreach (Microsoft.SqlServer.Management.Smo.StoredProcedure smoProc in StoredProcs.SelectedItems) {
						dotNails.CodeGen.Models.Function dbmlFunction = SmoUtil.GetDbmlFunctionFromSmoStoredProcedure(smoProc, conn, MappedTableIDs);
						newDbml.DB.Functions.Add(dbmlFunction);
					}
				}
				
				// Write out the whole dbml file - map the object to xml - Later, keep track of the original dbml, and merge the new with the old, honoring any data in the old
				//  TODO: Replace tables in old dbml with same name, but leave the rest alone (rewrite them all to xml)
				//  Merge new tables with existing tables in dbml
				DbmlUtil.MergeNewDbmlWithOld(newDbml, _dbml);
				
				// *** Code Generation *** 
				// Get the projects we will be dealing with
				// The modelProject will always be the project that the dbml was found in
				Project modelProject = SolutionUtil.GetProjectByName(_parent.DTE.Solution.Projects, _dbml.ProjectName);
				// The webProject will always be the web project in the solution (currently, if no web project is present, the project containing the dbml is used,
				//  but I think we will probably want to throw an exception if no web project is present in the solution, or else create a web project)
				//  The controllerProject will also be the web project.
				Project webProject = SolutionUtil.GetWebProject(_parent.DTE) ?? modelProject;
				
				string relativeModelFolder = AddInUtil.GetRelativeFolderPath(modelProject, _dbml.FilePath);
				string relativeControllerFolder = "Controllers";
				string relativeViewsFolder = "Views";	// This will be further modified by appending the controller name later
				string modelNamespace = AddInUtil.GetNamespaceFromFilePath(modelProject, _dbml.FilePath); // Model namespace must match dbml namespace
				string controllerNamespace = webProject.Name + "." + relativeControllerFolder;
				string viewNamespace = webProject.Name + "." + relativeViewsFolder;	// This will be further modified by appending the controller name later
				string relativeCommonFolder = "dotNailsCommon";
				string commonNamespace = modelProject.Name + "." + relativeCommonFolder;

				// Checkout the web project if under source control
				AddInUtil.CheckFileOutOfSourceControl(_parent.DTE, webProject.FileName);
					
				if (chkGenerateModel.IsChecked.Value) {
					XElement xml = _dbml.SerializeToXml();
					//XElement xml = newDbml.SerializeToXml();

					// Checkout the model project if under source control
					AddInUtil.CheckFileOutOfSourceControl(_parent.DTE, modelProject.FileName);
					// Checkout the dbml file if under source control
					AddInUtil.CheckFileOutOfSourceControl(_parent.DTE, _dbml.FilePath);
					
					xml.Save(_dbml.FilePath);
					//xml.Save(@"E:\Development\Test\HiveSingleFileGeneratorTest\HiveSingleFileGeneratorTest\DataClasses2.dbml");

					// TODO: Maintain dbml.layout.cs file for all location info for existing tables

					// Execute the custom tool for the dbml
					//DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate() 
					//DTE.ActiveWindow.Object.GetItem("Win32test\Win32test\ReadMe.txt").Select(vsUISelectionType.vsUISelectionTypeSelect) 
					//DTE.ExecuteCommand("Build.Compile") 
					//DTE.Windows.Item(EnvDTE80.WindowKinds.vsWindowKindErrorList).Activate() 
					_parent.RunCustomToolOnFile(_dbml.FilePath);

					// Generate partial files for all tables in the dbml (if they don't already exist)
					string modelProjectFolder = FileUtil.GetFolderPath(AddInUtil.GetProjectFilePath(modelProject));
					string dbmlFolderPath = FileUtil.GetFolderPath(_dbml.FilePath);
					Assembly assembly = this.GetType().Module.Assembly; // needed to retrieve embedded resources
					foreach (Microsoft.SqlServer.Management.Smo.Table smoTable in Tables.SelectedItems) {
						// Get the dbmlTable from the name
						dotNails.CodeGen.Models.Table dbmlTable = _dbml.DB.Tables.Find(t => t.Name == DbmlUtil.BuildDbmlTableName(smoTable.Schema, smoTable.Name));

						// Generate partial class for the entity itself
						AddInUtil.AddClassFromSimpleTemplate(dbmlTable.Type.Name, "PartialClass.cst",
															new Dictionary<string, string> { { "$Namespace$", modelNamespace }, { "$ClassName$", dbmlTable.Type.Name }, { "$CommonNamespace$", commonNamespace } },
															modelProject, relativeModelFolder, dbmlFolderPath, assembly);

						// Generate partial class for the Query class
						AddInUtil.AddClassFromSimpleTemplate(dbmlTable.Type.Name + "Query", "PartialQueryClass.cst",
															new Dictionary<string, string> { { "$Namespace$", modelNamespace }, { "$ClassName$", dbmlTable.Type.Name + "Query" }, { "$CommonNamespace$", commonNamespace } },
															modelProject, relativeModelFolder, dbmlFolderPath, assembly);

					} 
                    
					// Common files are created in the dotNailsCommon folder at the base of the Model project
					// Create the shared and base classes if they don't exist already
					AddInUtil.AddClassFromSimpleTemplate("EntityStateEnum", "EntityStateEnum.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("IEntity", "IEntity.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("IModelFactory", "IModelFactory.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("ModelFactory", "ModelFactory.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("LinqEntityBase", "LinqEntityBase.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					//AddInUtil.AddClassFromSimpleTemplate("SortedPagedList", "SortedPagedList.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("ManyToManyList", "ManyToManyList.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("DotNailsDataContext", "DotNailsDataContext.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("DotNailsProcedures", "DotNailsProcedures.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("DotNailsModelBinder", "DotNailsModelBinder.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace }, { "$ModelNamespace$", modelNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("IAuditable", "IAuditable.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("IDotNailsController", "IDotNailsController.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("IDotNailsValidationAttribute", "IDotNailsValidationAttribute.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("RunWhenEnum", "RunWhenEnum.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("ValidationAttributes", "ValidationAttributes.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("DotNailsHtmlHelpers", "DotNailsHtmlHelpers.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("SerializationUtil", "SerializationUtil.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace }, { "$ModelNamespace$", modelNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("DebugTextWriter", "DebugTextWriter.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
					AddInUtil.AddClassFromSimpleTemplate("LinqEntityBaseExtensions", "LinqEntityBaseExtensions.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
                    AddInUtil.AddClassFromSimpleTemplate("GenericSorter", "GenericSorter.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
                    AddInUtil.AddClassFromSimpleTemplate("IPagedList", "IPagedList.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
                    AddInUtil.AddClassFromSimpleTemplate("BasePagedList", "BasePagedList.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
                    AddInUtil.AddClassFromSimpleTemplate("PagedList", "PagedList.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
                    AddInUtil.AddClassFromSimpleTemplate("PagedListExtensions", "PagedListExtensions.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);
                    AddInUtil.AddClassFromSimpleTemplate("StaticPagedList", "StaticPagedList.cst", new Dictionary<string, string> { { "$Namespace$", commonNamespace } }, modelProject, relativeCommonFolder, modelProjectFolder, assembly);

					// Add required assembly references to the model project
					string sharedAssemblyPath;
					sharedAssemblyPath = Microsoft.Win32.Registry.GetValue(@"HKEY_LOCAL_MACHINE\Software\dotNails", "InstallationPath", string.Empty).ToString();
					if (string.IsNullOrEmpty(sharedAssemblyPath)) {
						throw new Exception(@"InstallationPath not found or is null in registry: HKEY_LOCAL_MACHINE\Software\dotNails\InstallationPath");
					}
					sharedAssemblyPath = Path.Combine(sharedAssemblyPath, "Shared"); 
					List<string> modelProjectReferences = new List<string> { "System.Configuration",
																			 "System.Web.Mvc",
																			 "System.Web.Routing",
																			 "System.Runtime.Serialization" };
					AddInUtil.AddAssemblyReferences(modelProject, modelProjectReferences);
					AddInUtil.AddAssemblyReferenceFromSpecificPath(modelProject, "System.ComponentModel.DataAnnotations", sharedAssemblyPath);
					AddInUtil.AddAssemblyReferenceFromSpecificPath(modelProject, "Microsoft.Web.Mvc.DataAnnotations", sharedAssemblyPath);
					AddInUtil.AddAssemblyReferenceFromSpecificPath(webProject, "Microsoft.Web.Mvc", sharedAssemblyPath);

					// Add required assembly references to the web project
					List<string> webProjectReferences = new List<string> { "System.Data.Linq" };
					AddInUtil.AddAssemblyReferences(webProject, webProjectReferences);
					// Add a reference to the Model project if it's not referenced already
					AddInUtil.AddProjectReference(webProject, modelProject);

					// Add ModelBinder code to Global.asax
					AddModelBinderCodeToGlobalAsax(webProject, commonNamespace);
					if (webProject != modelProject) {
						// Add Microsoft.Web.DataAnnotations to web project if we haven't already (if webProject != modelProject).  The code we added to Global.asax.cs requires it
						AddInUtil.AddAssemblyReferenceFromSpecificPath(webProject, "Microsoft.Web.Mvc.DataAnnotations", sharedAssemblyPath);
					}
				}

				// Figure out the full folder path to the web project
				string webProjectFolder = FileUtil.GetFolderPath(AddInUtil.GetProjectFilePath(webProject));
				
				// Create controllers if the user selected to scaffold them
				if (chkGenerateController.IsChecked.Value) {
					string fullDestinationFolder = Path.Combine(webProjectFolder, relativeControllerFolder);

					foreach (Microsoft.SqlServer.Management.Smo.Table smoTable in Tables.SelectedItems) {
						// Get the dbmlTable from the name
						dotNails.CodeGen.Models.Table dbmlTable = _dbml.DB.Tables.Find(t => t.Name == DbmlUtil.BuildDbmlTableName(smoTable.Schema, smoTable.Name));

						// Create a controller for each selected table
						TemplatedFile templatedFile = new TemplatedFile(FileTypeEnum.Controller, fullDestinationFolder, relativeControllerFolder, webProject.Name);
						templatedFile.FileName = dbmlTable.Type.Name + "Controller.cs";

						// Build CodeBlocks for Controller class - start with just one CodeBlock for entire file (all controllers will be the same to begin with)
						//  We can add functionality later to allow which Actions to add to the Controller.
						Controller controller = new Controller(dbmlTable, _dbml.DB.Class, controllerNamespace, modelNamespace, commonNamespace);
						templatedFile.CodeBlocks.Add(controller);

						// Render all the code blocks to strings and create the file
						templatedFile.GenerateFile();

						// Add the file to the project
						SolutionUtil.AddTemplatedFile(_parent.DTE, templatedFile);
					}
				}

				// Create the views if the user selected to scaffold them
				if (chkGenerateViews.IsChecked.Value) {
					// Create views for each selected table
					foreach (Microsoft.SqlServer.Management.Smo.Table smoTable in Tables.SelectedItems) {
						// Get the dbmlTable from the name
						dotNails.CodeGen.Models.Table dbmlTable = _dbml.DB.Tables.Find(t => t.Name == DbmlUtil.BuildDbmlTableName(smoTable.Schema, smoTable.Name));

						// Figure out the full file path to the folder within its destination project
						string viewFolder = relativeViewsFolder + "\\" + dbmlTable.Type.Name;
						string fullDestinationFolder = Path.Combine(webProjectFolder, viewFolder);
						viewNamespace = viewNamespace + "." + dbmlTable.Type.Name;

						GeneratePageView("Index", "Index", dbmlTable.Type.Name + " List", webProject, modelNamespace, commonNamespace, viewNamespace, viewFolder, fullDestinationFolder, dbmlTable);
						GeneratePageView("Show", "Show", dbmlTable.Type.Name + " Detail", webProject, modelNamespace, commonNamespace, viewNamespace, viewFolder, fullDestinationFolder, dbmlTable);
						GeneratePageView("New", "New", "Create a new " + dbmlTable.Type.Name, webProject, modelNamespace, commonNamespace, viewNamespace, viewFolder, fullDestinationFolder, dbmlTable);
						GeneratePageView("Edit", "Edit", "Edit an existing " + dbmlTable.Type.Name, webProject, modelNamespace, commonNamespace, viewNamespace, viewFolder, fullDestinationFolder, dbmlTable);
						GeneratePartialView("_" + StringUtil.GetCamelCaseName(dbmlTable.Type.Name) + "Form", "_form", string.Empty, webProject, modelNamespace, commonNamespace, viewNamespace, viewFolder, fullDestinationFolder, dbmlTable);
					}
				}
			}
			catch (Exception ex) {
				LogUtil.EventLogMessage(ex.Message, ex.InnerException, ex.StackTrace); 
			}

			//_parent.Close();
			ReloadTabs();
		}

		private void AddModelBinderCodeToGlobalAsax(Project webProject, string commonNamespace) {
			string webProjectFolder = FileUtil.GetFolderPath(AddInUtil.GetProjectFilePath(webProject));
			string globalAsaxFilePath = System.IO.Path.Combine(webProjectFolder, "global.asax.cs");
			string modelBinderSetter = @"
			ModelBinders.Binders.DefaultBinder = new " + commonNamespace + @".DotNailsModelBinder();";
			string contents = File.ReadAllText(globalAsaxFilePath);

			// Only write the method if there is no reference to DotNailsModelBinder() somewhere in the file already
			if (!contents.Contains("DotNailsModelBinder()")) {
				// Add code to set the default ModelBinder
				// First find the Application_Start method
				string methodToLookFor = "protected void Application_Start() {";
				int index = contents.IndexOf(methodToLookFor);

				if (index == -1) {
					// Didn't find Application_Start(), so don't do anything
					// TODO: Add a "tip" to somewhere, so we can advise the user about things they may need to do.
					return;
				}

				index += methodToLookFor.Length;
				
				// Insert the call to set the default ModelBinder
				contents = contents.Insert(index, modelBinderSetter);

				AddInUtil.CheckFileOutOfSourceControl(_parent.DTE, globalAsaxFilePath);

				// Write out the changed file
				File.WriteAllText(globalAsaxFilePath, contents);
			}
		}

		private void ReloadTabs() {
			if (tabTables.IsSelected) {
				LoadTables();
				ClearViews();
				ClearStoredProcs();
			}
			else if (tabViews.IsSelected) {
				LoadViews();
				ClearTables();
				ClearStoredProcs();
			}
			else if (tabStoredProcs.IsSelected) {
				LoadStoredProcs();
				ClearTables();
				ClearViews();
			}
		}

		private void GeneratePageView(string viewName, string templateName, string pageTitle, Project webProject, string modelNamespace, string commonNamespace, string viewNamespace, string viewFolder,
										string fullDestinationFolder, dotNails.CodeGen.Models.Table dbmlTable) {
			GenerateView(viewName, templateName, pageTitle, webProject, modelNamespace, commonNamespace, viewNamespace, viewFolder, fullDestinationFolder, dbmlTable, ViewEnum.Page);
		}

		private void GeneratePartialView(string viewName, string templateName, string pageTitle, Project webProject, string modelNamespace, string commonNamespace, string viewNamespace, string viewFolder,
										string fullDestinationFolder, dotNails.CodeGen.Models.Table dbmlTable) {
			GenerateView(viewName, templateName, pageTitle, webProject, modelNamespace, commonNamespace, viewNamespace, viewFolder, fullDestinationFolder, dbmlTable, ViewEnum.Partial);
		}

		private void GenerateView(string viewName, string templateName, string pageTitle, Project webProject, string modelNamespace, string commonNamespace, string viewNamespace, string viewFolder, 
									string fullDestinationFolder, dotNails.CodeGen.Models.Table dbmlTable, ViewEnum viewType) {
			T4TemplatedFile templatedFile = new T4TemplatedFile(FileTypeEnum.View, fullDestinationFolder, viewFolder, webProject.Name);
			string extension = (viewType == ViewEnum.Page) ? ".aspx" : ".ascx";
			templatedFile.FileName = viewName + extension;

			// Build CodeBlocks for Controller class - start with just one CodeBlock for entire file (all controllers will be the same to begin with)
			//  We can add functionality later to allow which Actions to add to the Controller.
			dotNails.CodeGen.Models.dotNails.View view = new dotNails.CodeGen.Models.dotNails.View(viewName, templateName, pageTitle, dbmlTable, _dbml.DB.Class, viewNamespace, modelNamespace, commonNamespace);
			templatedFile.CodeBlocks.Add(view);

			// Render all the code blocks to strings and create the file
			templatedFile.GenerateFile();

			// Add the file to the project
			SolutionUtil.AddTemplatedFile(_parent.DTE, templatedFile);
		}

		protected List<dotNails.CodeGen.Models.Table> GetRelatedDbmlTables(DbmlData existingDbml, DbmlData newDbml) {
			// By default, adding tables does not sync all existing dbml tables. Some models may have many tables, and the most common
			//  activity, besides syncing, will be to add to the model.  This method syncs any tables currently in the model that
			//  have relationships with the added tables and adds the table relationships to them.  It does not recursively sync any tables
			//  related to those tables.
			List<dotNails.CodeGen.Models.Table> relatedDbmlTables = new List<dotNails.CodeGen.Models.Table>();

			// Get smo objects, so we can read all smo info for the affected tables to get any changes.
			SqlConnection conn = new SqlConnection(existingDbml.DB.Connection.ConnectionString);
			Server server = new Server(new ServerConnection(conn));
			Database smoDb = SmoUtil.GetSmoDatabase(server, existingDbml.DB.Name);

			foreach (dotNails.CodeGen.Models.Table dbmlTable in newDbml.DB.Tables) {
				foreach (TableRelationship relationship in dbmlTable.TableRelationships) {
					// Don't look at paired relationships (their foreign key matchups are all fully defined within the new tables)
					if (!relationship.Paired) {
						dotNails.CodeGen.Models.Table tableInExistingDbml = null;
						TableRelationship newRelationship = null;

						if (relationship.IsReferencingTable) {
							// Check to see if the referenced table is within the old dbml
							string fullTableName = DbmlUtil.BuildDbmlTableName(relationship.ReferencedTableSchema, relationship.ReferencedTable);
							tableInExistingDbml = existingDbml.DB.Tables.SingleOrDefault(t => t.Name == fullTableName);

							if (tableInExistingDbml != null) {
								// Add the new relationship to the existing dbml table
								newRelationship = new TableRelationship();
								DbmlUtil.SetReferencedRelationshipFromReferencingRelationship(newRelationship, relationship, tableInExistingDbml.Type.Name);
								tableInExistingDbml.TableRelationships.Add(newRelationship);
							}
						}
						else {
							// Check to see if a referencing table is within the old dbml
							string fullTableName = DbmlUtil.BuildDbmlTableName(relationship.ReferencingTableSchema, relationship.ReferencingTable);
							tableInExistingDbml = existingDbml.DB.Tables.SingleOrDefault(t => t.Name == fullTableName);

							if (tableInExistingDbml != null) {
								// Add the new relationship to the existing dbml table
								newRelationship = new TableRelationship();
								DbmlUtil.SetReferencingRelationshipFromReferencedRelationship(newRelationship, relationship, tableInExistingDbml.Type.Name);
								tableInExistingDbml.TableRelationships.Add(newRelationship);
							}
						}

						if (tableInExistingDbml != null) {
							dotNails.CodeGen.Models.Table table = relatedDbmlTables.Find(t => t.Name == tableInExistingDbml.Name);
							// Don't add to relatedDbmlTables list if its already present in the list
							if (table == null) {
								tableInExistingDbml = SmoUtil.SyncDbmlTableWithDatabase(smoDb, tableInExistingDbml);
								relatedDbmlTables.Add(tableInExistingDbml);
							}
							//else {
							//    // Still need to add the new TableRelationship - I don't think so because the sync should get all the relationships
							//    table.TableRelationships.Add(newRelationship);
							//}
						}
					}
				}
			}

			return relatedDbmlTables;
		}

		private void chkSelectAllTables_Click(object sender, RoutedEventArgs e) {
			// The checkbox will immediately change its state when clicked, so coming in checked, means it was just checked
			if (!chkSelectAllTables.IsChecked.Value) {
				// Checkbox was deselected, so unselect all items in listbox
				Tables.UnselectAll();
			}
			else {
				_settingSelectAllTables = true;
				Tables.SelectAll();
				_settingSelectAllTables = false;
			}
		}

		private void Tables_SelectionChanged(object sender, RoutedEventArgs e) {
			if (!_settingSelectAllTables && chkSelectAllTables.IsChecked.Value) {
				chkSelectAllTables.IsChecked = false;
			}
		}

		private void chkSelectAllViews_Click(object sender, RoutedEventArgs e) {
			// The checkbox will immediately change its state when clicked, so coming in checked, means it was just checked
			if (!chkSelectAllViews.IsChecked.Value) {
				// Checkbox was deselected, so unselect all items in listbox
				Views.UnselectAll();
			}
			else {
				_settingSelectAllViews = true;
				Views.SelectAll();
				_settingSelectAllViews = false;
			}
		}

		private void Views_SelectionChanged(object sender, RoutedEventArgs e) {
			if (!_settingSelectAllViews && chkSelectAllViews.IsChecked.Value) {
				chkSelectAllViews.IsChecked = false;
			}
		}

		private void chkSelectAllProcs_Click(object sender, RoutedEventArgs e) {
			// The checkbox will immediately change its state when clicked, so coming in checked, means it was just checked
			if (!chkSelectAllProcs.IsChecked.Value) {
				// Checkbox was deselected, so unselect all items in listbox
				StoredProcs.UnselectAll();
			}
			else {
				_settingSelectAllProcs = true;
				StoredProcs.SelectAll();
				_settingSelectAllProcs = false;
			}
		}

		private void Procs_SelectionChanged(object sender, RoutedEventArgs e) {
			if (!_settingSelectAllProcs && chkSelectAllProcs.IsChecked.Value) {
				chkSelectAllProcs.IsChecked = false;
			}
		}

		private void ToggleSelectAllVisibility(DbManagerTabs selectedTab) {
			switch (selectedTab) {
				case DbManagerTabs.Tables:
					chkSelectAllTables.Visibility = Visibility.Visible;
					chkSelectAllViews.Visibility = Visibility.Hidden;
					chkSelectAllProcs.Visibility = Visibility.Hidden;
					break;
				case DbManagerTabs.Views:
					chkSelectAllTables.Visibility = Visibility.Hidden;
					chkSelectAllViews.Visibility = Visibility.Visible;
					chkSelectAllProcs.Visibility = Visibility.Hidden;
					break;
				case DbManagerTabs.Procs:
					chkSelectAllTables.Visibility = Visibility.Hidden;
					chkSelectAllViews.Visibility = Visibility.Hidden;
					chkSelectAllProcs.Visibility = Visibility.Visible;
					break;
			}
		}

	}

	public enum DbManagerTabs {
		Tables,
		Views,
		Procs
	}

	public enum ViewEnum {
		Page,
		Partial
	}
}
